package tr.edu.ege.kipler.common;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * This is actually a graph implementation.
 * 
 * @author İnanç Seylan
 */
public class RelationalModel<V extends RelationalValue<V, K>, R, K> implements
		Copyable<RelationalModel<V, R, K>> {

	private Map<K, RelationalNode<V, R, K>> nodeMap = new LinkedHashMap<K, RelationalNode<V, R, K>>();

	public RelationalModel() {
	}

	public RelationalModel<V, R, K> copy() {
		RelationalModel<V, R, K> model = new RelationalModel<V, R, K>();
		for (K key : nodeMap.keySet()) {
			model.nodeMap.put(key, getNode(key).copy());
		}
		for (RelationalNode<V, R, K> node : nodeMap.values()) {
			for (R rel : node.edgesOutgoing()) {
				for (RelationalNode<V, R, K> toNode : node.getOutgoing(rel)) {
					model.getNode(node.getValue().getKey()).addOutgoing(rel,
							model.getNode(toNode.getValue().getKey()));
				}
			}
			for (R rel : node.edgesIncoming()) {
				for (RelationalNode<V, R, K> fromNode : node.getIncoming(rel)) {
					model.getNode(node.getValue().getKey()).addIncoming(rel,
							model.getNode(fromNode.getValue().getKey()));
				}
			}
		}
		return model;
	}

	public RelationalNode<V, R, K> getNode(K key) {
		return nodeMap.get(key);
	}

	public Collection<RelationalNode<V, R, K>> getNodes() {
		return nodeMap.values();
	}

	public void add(RelationalNode<V, R, K> node) {
		nodeMap.put(node.getValue().getKey(), node);
	}

	public void remove(RelationalNode<V, R, K> node) {
		for (RelationalNode<V, R, K> owner : node.getIncoming()) {
			owner.removeOutgoing(node);
		}
		for (RelationalNode<V, R, K> filler : node.getOutgoing()) {
			filler.removeIncoming(node);
		}
		nodeMap.remove(node.getValue().getKey());
	}

	public boolean contains(K key) {
		return nodeMap.containsKey(key);
	}

	@Override
	public boolean equals(Object obj) {
		boolean result = false;
		if (obj != null && (obj.getClass().equals(this.getClass()))) {
			RelationalModel<?, ?, ?> other = (RelationalModel<?, ?, ?>) obj;
			result = nodeMap.equals(other.nodeMap);
		}
		return result;
	}

	@Override
	public String toString() {
		StringBuffer buffer = new StringBuffer();
		for (K key : nodeMap.keySet()) {
			buffer.append(getNode(key));
		}
		return buffer.toString();
	}

}
